//--------------------------------------------------------------------------------
// This file is a portion of the D2D1Renderer.  It is distributed
// under the MIT License, available in the root of this distribution and 
// at the following URL:
//
// http://www.opensource.org/licenses/mit-license.php
//
// Copyright (c) Mirco Timmermann
//
//--------------------------------------------------------------------------------
#pragma once

#include "..\..\Rendering\canvas\HTML5CanvasInterfaces\CanvasRenderingContext2D.h"
#include "..\..\Rendering\canvas\HTML5CanvasInterfaces\CanvasPathMethods.h"
#include "..\..\Rendering\canvas\Direct2DContext.h"

#include <vector>


namespace D2 {

class Command {
public:
	virtual void execute(Direct2DContext* ctx) = 0;

};

class Graphics : public CanvasRenderingContext2D, CanvasPathMethods {
private:
	class MoveTo : public Command {
	public:
		MoveTo(float x, float y) {
			_x = x;
			_y = y;
		}

		void execute(Direct2DContext* ctx) {
			ctx->moveTo(_x, _y);
		}

	private:
		float _x;
		float _y;

	};

	class LineTo : public Command {
	public:
		LineTo(float x, float y) {
			_x = x;
			_y = y;
		}

		void execute(Direct2DContext* ctx) {
			ctx->lineTo(_x, _y);
		}

	private:
		float _x;
		float _y;

	};

	class QuadraticCurveTo : public Command {
	public:
		QuadraticCurveTo(float cpx, float cpy, float x, float y) {
			_cpx = cpx;
			_cpy = cpy;
			_x = x;
			_y = y;
		}

		void execute(Direct2DContext* ctx) {
			ctx->quadraticCurveTo(_cpx, _cpy, _x, _y);
		}

	private:
		float _cpx;
		float _cpy;
		float _x;
		float _y;

	};

	class BezierCurveTo : public Command {
	public:
		BezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y) {
			_cp1x = cp1x;
			_cp1y = cp1y;
			_cp2x = cp2x;
			_cp2y = cp2y;
			_x = x;
			_y = y;
		}

		void execute(Direct2DContext* ctx) {
			ctx->bezierCurveTo(_cp1x, _cp1y, _cp2x, _cp2y, _x, _y);
		}

	private:
		float _cp1x;
		float _cp1y;
		float _cp2x;
		float _cp2y;
		float _x;
		float _y;

	};

	class ArcTo : public Command {
	public:
		ArcTo(float x1, float y1, float x2, float y2, float radius) {
			_x1 = x1;
			_y1 = y1;
			_x2 = x2;
			_y2 = y2;
			_radius = radius;
		}

		void execute(Direct2DContext* ctx) {
			ctx->arcTo(_x1, _y1, _x2, _y2, _radius);
		}

	private:
		float _x1;
		float _y1;
		float _x2;
		float _y2;
		float _radius;

	};

	class Rect : public Command {
	public:
		Rect(float x, float y, float w, float h) {
			_x = x;
			_y = y;
			_w = w;
			_h = h;
		}

		void execute(Direct2DContext* ctx) {
			ctx->rect(_x, _y, _w, _h);
		}

	private:
		float _x;
		float _y;
		float _w;
		float _h;

	};

	class Arc : public Command {
	public:
		Arc(float x, float y, float radius, float startAngle, float endAngle, bool anticlockwise) {
			_x = x;
			_y = y;
			_radius = radius;
			_startAngle = startAngle;
			_endAngle = endAngle;
			_anticlockwise = anticlockwise;
		}

		void execute(Direct2DContext* ctx) {
			ctx->arc(_x, _y, _radius, _startAngle, _endAngle, _anticlockwise);
		}

	private:
		float _x;
		float _y;
		float _radius;
		float _startAngle;
		float _endAngle;
		bool _anticlockwise;

	};

	class ClosePath : public Command {
	public:
		ClosePath() { }

		void execute(Direct2DContext* ctx) {
			ctx->closePath();
		}
	};

	class Fill : public Command {
	public:
		Fill() { }

		void execute(Direct2DContext* ctx) {
			ctx->fill();
		}
	};

	class Stroke : public Command {
	public:
		Stroke() { }

		void execute(Direct2DContext* ctx) {
			ctx->stroke();
		}
	};

	class Clip : public Command {
	public:
		Clip() { }

		void execute(Direct2DContext* ctx) {
			ctx->clip();
		}
	};

	class BeginPath : public Command {
	public:
		BeginPath() { }

		void execute(Direct2DContext* ctx) {
			ctx->beginPath();
		}
	};

	class Save : public Command {
	public:
		Save() { }

		void execute(Direct2DContext* ctx) {
			ctx->save();
		}
	};

	class Restore : public Command {
	public:
		Restore() { }

		void execute(Direct2DContext* ctx) {
			ctx->restore();
		}
	};

	class ClearRect : public Command {
	public:
		ClearRect(float x, float y, float w, float h) {
			_x = x;
			_y = y;
			_w = w;
			_h = h;
		}

		void execute(Direct2DContext* ctx) {
			ctx->clearRect(_x, _y, _w, _h);
		}

	private:
		float _x;
		float _y;
		float _w;
		float _h;

	};

	class FillRect : public Command {
	public:
		FillRect(float x, float y, float w, float h) {
			_x = x;
			_y = y;
			_w = w;
			_h = h;
		}

		void execute(Direct2DContext* ctx) {
			ctx->fillRect(_x, _y, _w, _h);
		}

	private:
		float _x;
		float _y;
		float _w;
		float _h;

	};

	class StrokeRect : public Command {
	public:
		StrokeRect(float x, float y, float w, float h) {
			_x = x;
			_y = y;
			_w = w;
			_h = h;
		}

		void execute(Direct2DContext* ctx) {
			ctx->strokeRect(_x, _y, _w, _h);
		}

	private:
		float _x;
		float _y;
		float _w;
		float _h;

	};

	class FillText : public Command {
	public:
		FillText(std::string text, float x, float y, float maxWidth) {
			_text = text;
			_x = x;
			_y = y;
			_maxWidth = maxWidth;
		}

		void execute(Direct2DContext* ctx) {
			ctx->fillText(_text, _x, _y, _maxWidth);
		}

	private:
		std::string _text;
		float _x;
		float _y;
		float _maxWidth;

	};

	class StrokeText : public Command {
	public:
		StrokeText(std::string text, float x, float y, float maxWidth) {
			_text = text;
			_x = x;
			_y = y;
			_maxWidth = maxWidth;
		}

		void execute(Direct2DContext* ctx) {
			ctx->strokeText(_text, _x, _y, _maxWidth);
		}

	private:
		std::string _text;
		float _x;
		float _y;
		float _maxWidth;

	};

	class SetStrokeStyle : public Command {
	public:
		SetStrokeStyle(float r, float g, float b) {
			_r = r;
			_g = g;
			_b = b;
		}

		void execute(Direct2DContext* ctx) {
			ctx->SetStrokeStyle(_r, _g, _b);
		}

	private:
		float _r;
		float _g;
		float _b;

	};

	class SetFillStyle : public Command {
	public:
		SetFillStyle(float r, float g, float b) {
			_r = r;
			_g = g;
			_b = b;
		}

		void execute(Direct2DContext* ctx) {
			ctx->SetFillStyle(_r, _g, _b);
		}

	private:
		float _r;
		float _g;
		float _b;

	};

	class SetLineWidth : public Command {
	public:
		SetLineWidth(float width) {
			_width = width;
		}

		void execute(Direct2DContext* ctx) {
			ctx->lineWidth = _width;
		}

	private:
		float _width;

	};

public:
	Graphics();
	virtual ~Graphics(void);

	void Execute(Direct2DContext* ctx);

private:
	std::vector<Command*> _commands;

private:
	void AddCommand(Command* command);

///////////////////////////////////////////////////////////////////////////////////////////////
//                          DRAW INTERFACE (CanvasRenderingContext2D)                        //
///////////////////////////////////////////////////////////////////////////////////////////////
	public:
		virtual void save();
		virtual void restore();
		virtual void clearRect(float x, float y, float w, float h);
		virtual void fillRect(float x, float y, float w, float h);
		virtual void strokeRect(float x, float y, float w, float h);
		virtual void beginPath();
		virtual void fill();
		virtual void stroke();
		virtual void clip();
		virtual void fillText(std::string text, float x, float y, float maxWidth = 0);
		virtual void strokeText(std::string text, float x, float y, float maxWidth = 0);

///////////////////////////////////////////////////////////////////////////////////////////////
//                                     PATH INTERFACE                                        //
///////////////////////////////////////////////////////////////////////////////////////////////
	public:
		// shared path API methods
		virtual void closePath();
		virtual void moveTo(float x, float y);
		virtual void lineTo(float x, float y);
		virtual void quadraticCurveTo(float cpx, float cpy, float x, float y);
		virtual void bezierCurveTo(float cp1x, float cp1y, float cp2x, float cp2y, float x, float y);
		virtual void arcTo(float x1, float y1, float x2, float y2, float radius);
		virtual void rect(float x, float y, float w, float h);
		virtual void arc(float x, float y, float radius, float startAngle, float endAngle, bool anticlockwise);

		virtual CanvasGradient createLinearGradient(float x0, float y0, float x1, float y1);
		virtual CanvasGradient createRadialGradient(float x0, float y0, float r0, float x1, float y1, float r1);

///////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                           //
///////////////////////////////////////////////////////////////////////////////////////////////
	public:
		void setStrokeStyle(float r, float g, float b);
		void setFillStyle(float r, float g, float b);
		void setLineWidth(float width);

};

}